#ifndef __DATA_HPP__
#define __DATA_HPP__
#include <unordered_map>
#include <pthread.h>
#include <bitset>
#include <vector>
#include <string>
#include <functional>
#include "util.hpp"
#include "config.hpp"

// 如何管理数据
// 1. 用于数据信息访问: 使用hash表在内存中管理数据，以url的path作为key值--查询速度快
// 2. 持久化存储管理: 使用json序列化将所有的数据信息保存在文件中

namespace cloud
{
    // 布隆过滤器：快速判断路径是否可能存在，减少无效哈希表查询
    class BloomFilter
    {
    private:
        std::bitset<1000000> bits;                                              // 位数组
        std::vector<std::function<size_t(const std::string &)>> hash_functions; // 多个哈希函数

    public:
        BloomFilter()
        {
            // 初始化3个不同的哈希函数
            hash_functions.push_back([](const std::string &s){
                std::hash<std::string> h;
                return h(s) % 1000000; 
            });
            hash_functions.push_back([](const std::string &s){
                size_t hash = 0;
                for (char c : s) hash = hash * 31 + c;
                return hash % 1000000; 
            });
            hash_functions.push_back([](const std::string &s){
                size_t hash = 0;
                for (char c : s) hash = hash * 131 + c;
                return hash % 1000000; 
            });
        }

        // 添加路径到过滤器
        void add(const std::string &path)
        {
            for (auto &hf : hash_functions)
            {
                bits.set(hf(path));
            }
        }

        // 判断路径是否可能存在（false表示一定不存在）
        bool might_contain(const std::string &path)
        {
            for (auto &hf : hash_functions)
            {
                if (!bits.test(hf(path)))
                {
                    return false; // 一定不存在
                }
            }
            return true; // 可能存在（有小概率误判）
        }
    };

    // 数据管理类: 管理服务端系统中会用到的数据
    // 1. 数据信息结构体:
    typedef struct BackupInfo
    {
        // 需要管理的信息
        bool pack_flag;        // 文件是否压缩的标志位: 判断文件是否已经被压缩了
        size_t fsize;          // 文件大小
        time_t mtime;          // 文件最后一次修改时间
        time_t atime;          // 文件最后一次访问时间
        time_t mem_atime;      // 内存数据访问时间（新增，用于内存热点判断）
        std::string real_path; // 文件的实际存储路径: 当客户端要下载文件时，则从这个文件中读取数据进行响应
        std::string pack_path; // 文件的压缩包存放路径名: 如果这个文件是一个非热点文件会被压缩，则这个就是压缩包路径名称
        std::string url;       // 文件资源路径path: 例如/download/a.txt

        // 获取realpath路径下的文件信息 填充信息
        // 新增备份信息
        bool NewBackupInfo(const std::string &realpath)
        {
            // 判断文件是否存在
            FileUtil fu(realpath);
            if (fu.Exists() == false)
            {
                std::cout << "new backupinfo: file not exist!" << std::endl;
                return false;
            }

            Config *config = Config::GetInstance();
            std::string packdir = config->GetPackDir();
            std::string packsuffix = config->GetPackFileSuffix();
            std::string download_prefix = config->GetDownloadPrefix();
            this->pack_flag = false; // 新增，非热点文件
            this->fsize = fu.FileSize();
            this->mtime = fu.LastMTime();
            this->atime = fu.LastATime();
            this->mem_atime = time(nullptr); // 初始化内存访问时间为当前时间
            this->real_path = realpath;
            // ./backdir/a.txt -> ./packdir/a.txt.lz
            // 两个信息1.压缩包路径 + 文件名 + 2.压缩包后缀名
            // 因为变成热点文件之后路径名和文件后缀都会改变
            //./
            this->pack_path = packdir + fu.FileName() + packsuffix;
            // ./backdir/a.txt -> ./download/a.txt
            // 下载前缀 + 文件名
            this->url = download_prefix + fu.FileName(); // 备份的文件资源路径统一都是 下载请求

            return true;
        }
    } BackupInfo;

    // 2. 数据管理类
    class DataManager
    {
    private:
        std::string _backup_file;                           // 持久化存储文件
        pthread_rwlock_t _rwlock;                           // 读写锁--读共享，写互斥  为什么不使用互斥锁，串行访问，不合适，可能有多个线程只是像读取文件的信息
        std::unordered_map<std::string, BackupInfo> _table; // 内存中以hash表储存
        int _mem_hot_time;                                  // 内存热点时间阈值（配置项，单位：秒）

        // 对不存在的路径查询，通过布隆过滤器在加锁查询哈希表前就直接返回，减少无效的锁竞争和哈希表访问，尤其适合大量不存在路径的查询场景
        BloomFilter _bloom_filter;
        // 内存热点管理
    public:
        // 更新内存数据的访问时间（每次访问内存数据时调用）
        bool UpdateMemAtime(const std::string &url)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.find(url);
            if (it != _table.end())
            {
                it->second.mem_atime = time(nullptr); // 更新为当前时间
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        // 判断内存数据是否为非热点（超过内存热度阈值）
        bool IsMemNonHot(const BackupInfo &info)
        {
            time_t cur_time = time(nullptr);
            return (cur_time - info.mem_atime > _mem_hot_time);
        }

        // 清理内存中的非热点数据（保留磁盘文件，仅释放内存）
        bool CleanMemNonHot()
        {
            pthread_rwlock_wrlock(&_rwlock);
            std::vector<std::string> to_remove;
            for (auto &pair : _table)
            {
                if (IsMemNonHot(pair.second))
                {
                    to_remove.push_back(pair.first); // 记录需清理的url
                }
            }
            for (auto &url : to_remove)
            {
                _table.erase(url); // 从内存中删除非热点数据
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

    public:
        DataManager()
        {
            _backup_file = Config::GetInstance()->GetBackupFile();
            _mem_hot_time = Config::GetInstance()->GetMemHotTime(); // 从配置获取内存热点阈值
            pthread_rwlock_init(&_rwlock, nullptr);                 // 初始化读写锁
            InitLoad();                                             // 加载所有文件备份信息到哈希表中
        }
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock); // 销毁读写锁
        }
        bool Insert(const BackupInfo &info) // 新增文件信息
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url] = info; // 将文件信息存放到哈希表中
            _bloom_filter.add(info.real_path);  // 同步添加到过滤器
            pthread_rwlock_unlock(&_rwlock);
            Storage(); // 进行持久化存储
            return true;
        }
        bool Update(const BackupInfo &info) // 修改文件信息 当数据超过热点管理时间之后，我们需要修改文件信息，因为文件已经被压缩了
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();
            return true;
        }

        // 当客户下载文件时只需要获取一个文件的信息
        bool GetOneByURL(const std::string &url, BackupInfo *info) // 根据文件资源路径获取文件信息
        {
            pthread_rwlock_rdlock(&_rwlock);
            // 因为url是key值，所以直接通过find进行查找
            auto it = _table.find(url); // 先查找资源路径
            if (it == _table.end())
            {
                pthread_rwlock_unlock(&_rwlock); // 退出函数记得解锁
                return false;
            }
            *info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        // 情况: 存储时，文件真实存储了，但是恰好把某个数据丢了，没有存进去

        // 实际文件已经成功保存到了存储系统中（比如硬盘上确实确实确实存在这个文件）
        // 但是在程序的_table（看起来是存储文件信息的哈希表 / 映射表）中，却没有记录这个文件的相关信息（BackupInfo）
        // 或者可能是记录了部分信息，但关键数据丢失了（比如路径、大小等字段不完整）

        //这个文件是客户端上传的文件，服务端这边需要保存的，使用实际路径遍历哈希表，如果没有找到，则说明该文件存在磁盘上，但是并没有在哈希表上进行维护
        //为什么使用实际路径：绝对路径，是文件在存储介质上的唯一标识，具有很强的确定性
        bool GetOneByRealPath(const std::string &realpath, BackupInfo *info) // 根据真实路径获取文件信息
        {
            // 先通过布隆过滤器快速排除不存在的路径
            if (!_bloom_filter.might_contain(realpath))
            {
                return false; // 无需加锁，直接返回
            }

            pthread_rwlock_rdlock(&_rwlock);
            auto it = _table.begin();
            for (; it != _table.end(); ++it)
            {
                if (it->second.real_path == realpath)
                {
                    *info = it->second;
                    pthread_rwlock_unlock(&_rwlock);

                    return true;
                }
            }
            pthread_rwlock_unlock(&_rwlock);

            return false;
        }

        bool GetAll(std::vector<BackupInfo> *arry) // 获取所有信息
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            for (; it != _table.end(); ++it)
            {
                arry->push_back(it->second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        bool Storage() // 每次数据新增或修改都要重新持久化存储，避免数据丢失
        {
            // 1. 获取所有数据
            std::vector<BackupInfo> arry;
            this->GetAll(&arry);
            // 2. 添加到Json::Value
            Json::Value root;
            for (int i = 0; i < arry.size(); i++)
            {
                Json::Value item;
                item["pack_flag"] = arry[i].pack_flag;
                item["fsize"] = (Json::Int64)arry[i].fsize;
                item["atime"] = (Json::Int64)arry[i].atime;
                item["mtime"] = (Json::Int64)arry[i].mtime;
                item["mem_atime"] = (Json::Int64)arry[i].mem_atime;
                item["real_path"] = arry[i].real_path;
                item["pack_path"] = arry[i].pack_path;
                item["url"] = arry[i].url;
                root.append(item); // 添加数据元素
            }
            // 3. 对Json::value序列化
            std::string body;
            JsonUtil::Serialize(root, &body); // 将文件数据放到body中
            // 4. 写文件
            FileUtil fu(_backup_file); // 使用备份文件初始化工具类
            fu.SetContent(body);       // 将数据设置进工具类中
            return true;
        }

        bool InitLoad() // 初始化加载，在每次系统重启都要加载一起的数据
        {
            // 1. 将数据文件中的数据读取出来
            FileUtil fu(_backup_file);
            if (fu.Exists() == false) // 判断有没有数据，没有数据是有问题的
            {
                return true;
            }
            std::string body;
            fu.GetContent(&body);

            // 2. 反序列化
            Json::Value root;
            JsonUtil::UnSerialize(body, &root);

            // 3. 将反序列化得到的Json::Value中的数据添加到table中
            for (int i = 0; i < root.size(); i++)
            {
                BackupInfo info; // 将文件数据重新加载到info中
                info.pack_flag = root[i]["pack_flag"].asBool();
                info.fsize = root[i]["fsize"].asInt64();
                info.atime = root[i]["atime"].asInt64();
                info.mtime = root[i]["mtime"].asInt64();
                info.mem_atime = root[i]["mem_atime"].asInt64();
                info.pack_path = root[i]["pack_path"].asString();
                info.real_path = root[i]["real_path"].asString();
                info.url = root[i]["url"].asString();
                Insert(info); // 最后重新将info插入到哈希表中即可
            }
            return true;
        }
    };

}

#endif
